//
// Unihan Input Method
//
// Copyright © 2002-2007 Sun Wah Linux Ltd.
//
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
// USA
//
// $Id: illegibility.c 757 2007-05-10 08:19:31Z roger $
//

/* zh	z
 * ch	c
 * sh	s
 * n	l
 * l	r
 * f	h
 * f	hu
 * wang	huang
 * an	ang
 * en	eng
 * in	ing
 * This is for some one can't recognize these pronouciation.
 * Preedit display what users input.
 * Candidates bar will pick up all candidates users don't know.
 * e.x inputing 'z' will get candidates belong to 'z' and 'zh'
 * I put all these candidates in lookup draw buffer.
 *
 * When use SHUANGPIN preedit will follow SPTable.
 * When freqencing caculating priority will follow candidates' own familly
 * In another word , they will not caculate together.
 * 	Arren.	2004.3.27
 */

#include <string.h>
#include <stdio.h>

#include "illegibility.h"
#include "table.h"
#include "const.h"
#include "ci.h"

char *Ill_get_the_other_cell(char *keys, char **other);
static int Ill_is_cell_in_IllTable(IllTable *itable, char *cell);

static IllTable illTable[11] = {
	{"wang", "huang"},
	{"an", "ang"},
	{"en", "eng"},
	{"in", "ing"},
	{"zh", "z"},
	{"ch", "c"},
	{"sh", "s"},
	{"n", "l"},
	{"l", "r"},
	{"f", "h"},
	{"f", "hu"}
};

void
Ill_refresh_CandFactory(UnihanIM *uim, char *cell)
{
	TableDataPerSession *ds = (TableDataPerSession *)uim->sessionUserData;
	CandFactory *candFactory = ds->candFactory;
	char *now = NULL;	/* finded string in keys */
	char *other = NULL;	/* finded string in IllTable */
	MultiCandPos *mcp = NULL;

	if (candFactory->multiCandPos == NULL) {
		candFactory->multiCandPos = (MultiCandPos *)malloc(sizeof(MultiCandPos));
		memset(candFactory->multiCandPos, 0, sizeof(MultiCandPos));
		candFactory->numMultiCandPos = 1;
	}

	if (cell[0] == '-')
		CF_erase_keys(candFactory);
	else {
		mcp = candFactory->multiCandPos;
		while (mcp) {
			strcat(mcp->preedit, cell);
			mcp = mcp->next;
		}
	}

	if (candFactory->keys[0] != 0)
		sprintf(candFactory->keys, "%s%s", candFactory->keys, cell);
	else
		strcpy(candFactory->keys, cell);

	now = Ill_get_the_other_cell(candFactory->keys, &other);

	if (other && now)
		CF_erase_keys_with_cell(candFactory, now, other);
	if (candFactory->freqCandList) {
		free(candFactory->freqCandList);
		candFactory->freqCandList = NULL;
	}
}

char *
Ill_get_the_other_cell(char *keys, char **other)
{
	char *pChar = NULL;
	int i;
	int flag = 0;

	for (i=0; i<11; i++) {
		if (pChar = strstr(keys, illTable[i].str1))
			break;
		if (pChar = strstr(keys, illTable[i].str2)) {
			flag = 1;
			break;
		}
	}
	if (pChar) {
		if (flag == 1)
			*other = illTable[i].str1;
		else
			*other = illTable[i].str2;
		return pChar;
	}
	else {
		*other = NULL;
		return NULL;
	}
}

static int
Ill_is_cell_in_IllTable(IllTable *itable, char *cell)
{
	int i;

	for (i=0; i<11; i++) {
		if (strcmp(cell, illTable[i].str1) == 0)
			return 1;
		if (strcmp(cell, illTable[i].str2) == 0)
			return 2;
	}
	return 0;
}

void
Ill_find_candidates(UnihanIM *uim)
{
	TableDataPerDesktop *dd = (TableDataPerDesktop *)uim->desktopUserData;
	TableDataPerSession *ds = (TableDataPerSession *)uim->sessionUserData;
	CandFactory *candFactory = ds->candFactory;
	MultiCandPos *mcp = candFactory->multiCandPos;
	int low, high, exactIdx, i;
	KeyValTab *kv = NULL;

	candFactory->numTotalCands = 0;
	while (mcp) {
		mcp->preeditLen = strlen(mcp->preedit);
	        if (mcp->preeditLen == 0) {
			mcp = mcp->next;
	               	continue;
		}
		low = dd->fastIdx[idx(mcp->preedit[0])].offset;
		high = dd->fastIdx[idx(mcp->preedit[0])+1].offset - 1;
        	i = (low + high)/2;

		// binary search
		if (mcp->preeditLen != 1) {
			while (mcp->preeditLen > 1) {
				int cmp;
				
				if (high < low) {
					mcp->numExactCands = mcp->highIdx =
						mcp->lowIdx = mcp->exactIdx = -1;
					mcp->numCands = 0;
					break;
				}

				i = (low + high)/2;

				kv = &(dd->kvtab[i]);
				cmp = strncmp(mcp->preedit + 1,
					dd->keyList+(kv->keyOff) + 1,
					mcp->preeditLen-1);
				if (cmp < 0)
					high = i - 1;
				else if (cmp > 0)
					low = i + 1;
				else // cmp == 0!!! found!
					break;
			}
		}

		// find the real upper and lower ranges
		low = i - 1;
		high = i + 1;
		while (low >= 0) {
			KeyValTab *kv1 = &(dd->kvtab[low]);
			int cmp = strncmp(mcp->preedit,
					dd->keyList+(kv1->keyOff),
					mcp->preeditLen);
			if (cmp != 0) // found start of range
				break;
			--low;
		}
		++low;
		while (high < dd->numVal) {
			KeyValTab *kv1 = &(dd->kvtab[high]);
			int cmp = strncmp(mcp->preedit,
					dd->keyList+(kv1->keyOff),
					mcp->preeditLen);
			if (cmp != 0)
				break;
			++high;
		}

		// find how many are exact matches
		kv = &(dd->kvtab[low]);
		if (mcp->preeditLen < strlen(dd->keyList + kv->keyOff)) {
			exactIdx = low + 1;
		} else {
			exactIdx = low + 1;
			while (exactIdx < high) {
				kv = &(dd->kvtab[exactIdx]);
				if (*(dd->keyList+kv->keyOff+mcp->preeditLen) != 0)
					break;
				++exactIdx;
			}
		}

		mcp->lowIdx = low;
		mcp->highIdx = high;
		mcp->exactIdx = exactIdx;
		mcp->numExactCands = exactIdx - low;
		if (dd->hints & SHOW_AS_U_GO)
			mcp->numCands = high - low;
		else
			mcp->numCands = mcp->numExactCands;
		candFactory->numTotalCands += mcp->numCands;
		mcp = mcp->next;
	}
	candFactory->freqCandList =
		(FreqCandList *)malloc(candFactory->numTotalCands * sizeof(FreqCandList));
	for (i=0; i<candFactory->numTotalCands; i++)
		candFactory->freqCandList[i].id = i;
	candFactory->luCurrCandOffset = 0;
	candFactory->word = get_word(uim);
	if (candFactory->word)
		candFactory->numTotalCands += 1;
	CF_display_tree(candFactory);
}

void
Ill_lookup_draw(UnihanIM *uim)
{
	TableDataPerDesktop *dd = (TableDataPerDesktop *)uim->desktopUserData;
	TableDataPerSession *ds = (TableDataPerSession *)uim->sessionUserData;
	CandFactory *candFactory = ds->candFactory;
	MultiCandPos *mcp = candFactory->multiCandPos;
	UnihanLookupDraw ludraw;
	int i=0,j;
	int numCands = 0;

	if (ds->lookupShown == 0) {
		UnihanLookupStart lustart;
		lustart.choicePerWindow = 9;
		lustart.numColumns = 9;
		lustart.numRows = 1;
		lustart.direction = UNIHAN_LOOKUP_DIRECTION_RIGHT;
		uim->m->lookup_start(uim, &lustart);
		ds->lookupShown = 1;
	}

	if ((candFactory->luCurrCandOffset / 9) * 9 + 9 >= candFactory->numTotalCands)
		ludraw.numChoices = candFactory->numTotalCands -
			(candFactory->luCurrCandOffset / 9) * 9;
	else
		ludraw.numChoices = 9;
	ludraw.selected = candFactory->luCurrCandOffset % 9;
        ludraw.currentChoice = candFactory->luCurrCandOffset; 
        ludraw.totalChoices = candFactory->numTotalCands;
	ludraw.title = "";
	uim->m->mem_alloc(uim, sizeof(char *) * ludraw.numChoices, (void**)&ludraw.label);
	uim->m->mem_alloc(uim, sizeof(char *) * ludraw.numChoices, (void**)&ludraw.value);

	//add user define word
	if (candFactory->word && !candFactory->luCurrCandOffset) {
		UnihanTCSCMode tcsc;
		char *cand = NULL;
		char *key = NULL;
		char *pehint = NULL;
		char *converted = NULL;
		int len = 0;

		i = 0;

		uim->m->mem_alloc(uim, sizeof(char)*2, (void**)&(ludraw.label[0]));
		ludraw.label[0][0] = '1';
		ludraw.label[0][0] = 0;

		cand = candFactory->word;
		key = "";

		len = strlen(cand) + 1;

                uim->m->tcsc_mode_get(uim, &tcsc);
                if (tcsc != UNIHAN_TCSC_NO_CONV) {
                        uim->m->tcsc_do_conv(uim, tcsc, cand, &converted);

                        if (!strcmp(converted, cand)) {
                                converted = NULL;
                        } else {
                                len += strlen(converted) + 5; // for the arrow
                        }
                }

                uim->m->mem_alloc(uim, len + 1, (void**)&(ludraw.value[0]));
                memset(ludraw.value[i], 0, len + 1);
                if (pehint) {
                        strcat(ludraw.value[i], pehint);
                        strcat(ludraw.value[i], " ");
                }
                strcat(ludraw.value[i], cand);
                if (converted) {
                        strcat(ludraw.value[i], " \342\206\222 "); // U+2192 right arrow
                        strcat(ludraw.value[i], converted);
                }
		numCands = 1;
	}

	for (i=numCands, j=0; i<ludraw.numChoices; ++i, ++j) {
		int index;
                UnihanTCSCMode tcsc;
                char *cand;
                char *key;
                char *pehint = NULL;
                char *converted = NULL;
                int len = 0;
		int id;

		uim->m->mem_alloc(uim, sizeof(char)*2, (void**)&(ludraw.label[i]));
		ludraw.label[i][0] = i + '1';
		ludraw.label[i][1] = 0;

		if (dd->FreqMode)
			id = candFactory->freqCandList[((candFactory->luCurrCandOffset / 9) * 9) +
				j].id;
		else
			id = ((candFactory->luCurrCandOffset/9)*9) + j;
		index = CF_get_cand_idx_by_id(candFactory, id);

                cand = dd->valList + dd->kvtab[index].valOff;

                key = dd->keyList + dd->kvtab[index].keyOff;

                len = strlen(cand);

                if (mcp->preeditLen < strlen(key)) {
                        pehint = key + ds->preeditLen;
                        len += strlen(pehint) + 1;
                }

                uim->m->tcsc_mode_get(uim, &tcsc);
                if (tcsc != UNIHAN_TCSC_NO_CONV) {
                        uim->m->tcsc_do_conv(uim, tcsc, cand, &converted);

                        if (!strcmp(converted, cand)) {
                                converted = NULL;
                        } else {
                                len += strlen(converted) + 5; // for the arrow
                        }
                }

                uim->m->mem_alloc(uim, len + 1, (void**)&(ludraw.value[i]));
                memset(ludraw.value[i], 0, len + 1);
                if (pehint) {
                        strcat(ludraw.value[i], pehint);
                        strcat(ludraw.value[i], " ");
                }
                strcat(ludraw.value[i], cand);
                if (converted) {
                        strcat(ludraw.value[i], " \342\206\222 "); // U+2192 right arrow
                        strcat(ludraw.value[i], converted);
                }
	}

	ludraw.labelStyles = NULL;
	ludraw.valueStyles = NULL;
	
	uim->m->lookup_draw(uim, &ludraw);
}

void
Ill_back_space(UnihanIM *uim)
{
	TableDataPerSession *ds = (TableDataPerSession *)uim->sessionUserData;
	CandFactory *candFactory = ds->candFactory;
	MultiCandPos *mcp = candFactory->multiCandPos;
	InputCell *ic = mcp->inputCell;
	char *cell;
	int i;

	if (candFactory->keys[0] != 0) {
		candFactory->keys[strlen(candFactory->keys) - 1] = 0;
		while(mcp) {
			mcp->preedit[strlen(mcp->preedit) - 1] = 0;
			mcp = mcp->next;
		}
		return;
	}

	while (ic->next)
		ic = ic->next;

	cell = ic->cell;

	if (Ill_is_cell_in_IllTable(illTable, cell))
		CF_half_MultiCandPos(candFactory);
	CF_set_the_last_InputCell_to_keys(candFactory);
	CF_all_throw_a_InputCell(candFactory);
	candFactory->keys[strlen(candFactory->keys) - 1] = 0;
	while (mcp) {
		CF_intergrate_cells_to_preedit(mcp);
		mcp = mcp->next;
	}
}

int
Ill_idx_search(UnihanIM *uim)
{
	TableDataPerSession *ds = (TableDataPerSession *)uim->sessionUserData;
	CandFactory *candFactory = ds->candFactory;
	int id;
	int idx = 0;

	if (candFactory->word && (candFactory->luCurrCandOffset < 9))
		return -1;

	id = candFactory->freqCandList[candFactory->luCurrCandOffset].id;
	idx = CF_get_cand_idx_by_id(candFactory, id);
	return idx;
}

int
Ill_idx_sel(UnihanIM *uim, IMKeyEvent *kev)
{
	TableDataPerSession *ds = (TableDataPerSession *)uim->sessionUserData;
	CandFactory *candFactory = ds->candFactory;
	int sel = kev->keyChar - '1';
	int id;
	int idx = 0;

	if (candFactory->word && (candFactory->luCurrCandOffset < 9) && !sel)
		return -1;

	if (candFactory->word)
		sel--;
	id = candFactory->freqCandList[candFactory->luCurrCandOffset/9*9 + sel].id;
	idx = CF_get_cand_idx_by_id(candFactory, id);
	return idx;
}

int
Ill_page_down(UnihanIM *uim)
{
	TableDataPerSession *ds = (TableDataPerSession *)uim->sessionUserData;
	CandFactory *candFactory = ds->candFactory;

	if (candFactory->numTotalCands <= 9) {
		uim->m->beep(uim);
		return 0;
	}

	candFactory->luCurrCandOffset += 9 - (candFactory->luCurrCandOffset % 9);
	if (candFactory->luCurrCandOffset > candFactory->numTotalCands)
		candFactory->luCurrCandOffset = 0;
	return 1;
}

int
Ill_page_up(UnihanIM *uim)
{
	TableDataPerSession *ds = (TableDataPerSession *)uim->sessionUserData;
	CandFactory *candFactory = ds->candFactory;

	if (candFactory->numTotalCands <= 9) {
		uim->m->beep(uim);
		return 0;
	}

	candFactory->luCurrCandOffset -= 9 + (candFactory->luCurrCandOffset % 9);
	if (candFactory->luCurrCandOffset < 0) {
		int offset = candFactory->numTotalCands % 9;
		if (offset == 0) offset = 9;
		candFactory->luCurrCandOffset = candFactory->numTotalCands - offset;
	}
	return 1;
}

void
Ill_sel_up(UnihanIM *uim)
{
	TableDataPerSession *ds = (TableDataPerSession *)uim->sessionUserData;
	CandFactory *candFactory = ds->candFactory;

	if (candFactory->luCurrCandOffset % 9 == 0)
		candFactory->luCurrCandOffset += 8;
	else
		candFactory->luCurrCandOffset--;
	if (candFactory->luCurrCandOffset >= candFactory->numTotalCands)
		candFactory->luCurrCandOffset = candFactory->numTotalCands - 1;
}

void
Ill_sel_down(UnihanIM *uim)
{
	TableDataPerSession *ds = (TableDataPerSession *)uim->sessionUserData;
	CandFactory *candFactory = ds->candFactory;

	if (candFactory->luCurrCandOffset % 9 == 8)
		candFactory->luCurrCandOffset -= 8;
	else
		candFactory->luCurrCandOffset++;
	if (candFactory->luCurrCandOffset >= candFactory->numTotalCands)
		candFactory->luCurrCandOffset -= candFactory->luCurrCandOffset % 9;
}

void
illegibility_test(UnihanIM *uim)
{
	TableDataPerDesktop *dd = (TableDataPerDesktop *)uim->desktopUserData;
	TableDataPerSession *ds = (TableDataPerSession *)uim->sessionUserData;
	int i,j;


	for(i=0; i<7; i++) {
		for (j=0; j<27; j++) {
			if (strlen(dd->sp_table[0][j].yun1) > 0) {
				strcpy(ds->preedit, illTable[i].str1);
				strncat(ds->preedit, 
					dd->sp_table[0][j].yun1,
					sp_strlen(dd->sp_table[0][j].yun1, 
						YUNMU));
				ds->preeditLen = strlen(ds->preedit);
				find_candidates(uim);
				if (ds->numCands > 0) {
				}
			}

			if (strlen(dd->sp_table[0][j].yun2) > 0) {
				strcpy(ds->preedit, illTable[i].str1);
				strncat(ds->preedit, dd->sp_table[0][j].yun2,
					sp_strlen(dd->sp_table[0][j].yun2,
						YUNMU));
				ds->preeditLen = strlen(ds->preedit);
				find_candidates(uim);
			}

			if (sp_strlen(dd->sp_table[0][j].yun1, YUNMU) > 0) {
				strcpy(ds->preedit, illTable[i].str2);
				strncat(ds->preedit, dd->sp_table[0][j].yun1,
					sp_strlen(dd->sp_table[0][j].yun1,
						YUNMU));
				ds->preeditLen = strlen(ds->preedit);
				find_candidates(uim);
			}

			if (strlen(dd->sp_table[0][j].yun2) > 0) {
				strcpy(ds->preedit, illTable[i].str2);
				strncat(ds->preedit, dd->sp_table[0][j].yun2,
					sp_strlen(dd->sp_table[0][j].yun2,
						YUNMU));
				ds->preeditLen = strlen(ds->preedit);
				find_candidates(uim);
			}
		}
	}
}


// vi:ts=4:nowrap:cin:expandtab
